/*
作者：吴咏炜
链接：https://www.zhihu.com/question/533559296/answer/2495020217
来源：知乎
著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。
*/

#include <iostream>
#include <memory>
#include <string>
#include <utility>
#include <variant>

namespace lisp {

namespace impl {

class atom;
class list;

void print(const atom&);
void print(const list&);

}  // namespace impl

using object = std::variant<impl::list, impl::atom>;
using pointer = std::shared_ptr<const object>;
using list_pointer = std::shared_ptr<const impl::list>;

void print(const pointer& data);
void print(const list_pointer& data);

namespace impl {
    using namespace std;
    class atom {
    public:
        atom(int value) : _content(value) {}
        atom(double value) : _content(value) {}
        atom(const char* name) : _content(std::string(name)) {}
        atom(std::string name) : _content(std::move(name)) {}

        void print(const atom& data) const
        {
            std::cout<<__repr__()<<std::endl;
        }

        std::string __repr__() const 
        {
            if(_content.index()==0) {
                return std::to_string(std::get<0>(_content));
            } else if(_content.index()==1) {
                return std::to_string(std::get<1>(_content));
            } else if(_content.index()==2) {
                return std::move(std::get<2>(_content));
            }
            std::cerr<<"error atom type"<<std::endl;
            exit(1);
        }
    private:
        std::variant<int, double, std::string> _content;
    };

    std::ostream& operator<<(std::ostream& os, const atom& a)
    {
        return os<< a.__repr__();
    }


    class list {
    public:
        list(pointer head, list_pointer tail)
            : head_(std::move(head)), tail_(std::move(tail))
        {}

        pointer head() const
        {
            return head_;
        }
        list_pointer tail() const
        {
            return tail_;
        }

    private:
        pointer head_;
        list_pointer tail_;
    };



    void print(const atom& data) 
    {
        using namespace std;
        cout << data <<endl;
    }

void print(const list& data)
{
    std::cout << "(";
    lisp::print(data.head());
    std::cout << ", ";
    lisp::print(data.tail());
    std::cout << ")";
}

}  // namespace impl

void print(const pointer& ptr)
{
    std::visit([](const auto& data) { print(data); }, *ptr);
}

void print(const list_pointer& ptr)
{
    if (ptr) {
        print(*ptr);
    } else {
        std::cout << "NIL";
    }
}

list_pointer cons(pointer head, list_pointer tail)
{
    return std::make_shared<impl::list>(std::move(head), std::move(tail));
}

list_pointer cons(impl::atom head, list_pointer tail)
{
    return std::make_shared<impl::list>(
        std::make_shared<object>(std::move(head)), std::move(tail));
}

pointer car(list_pointer lst)
{
    return lst->head();
}

list_pointer cdr(list_pointer lst)
{
    return lst->tail();
}

}  // namespace lisp




int main()
{
    using lisp::car;
    using lisp::cdr;
    using lisp::cons;
    using lisp::print;

    print(cons(3, cons("abc", cons(3.3, nullptr))));
    std::cout << std::endl;
    print(car(cons(3, cons("abc", cons(3.3, nullptr)))));
    std::cout << std::endl;
    print(cdr(cons(3, cons("abc", cons(3.3, nullptr)))));
    std::cout << std::endl;
    print(car(cdr(cons(3, cons("abc", cons(3.3, nullptr))))));
    std::cout << std::endl;
}